package play;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.ArrayList;

import mech.AttackQ;
import mech.UsefulMethods;
import obj.BossEnemy;
import obj.JumpingEnemy;
import obj.MovingEnemy;
import obj.MovingPlatform;
import obj.Platform;
import obj.Projectile;
import obj.ShootingEnemy;
import obj.StatueEnemy;

import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.state.BasicGameState;
import org.newdawn.slick.state.StateBasedGame;

import util.Rect;
import util.Settings;
import util.WorldLoader;

/**
 * The world of the game.
 * Main portion of where the player explores.
 * @author VincentLy
 */
public class World extends BasicGameState{ 

	private long savetime = 2000;
	private PrintWriter pw;
	private Rect savept;
	private ArrayList<Rect> hotspots;
	private Platform progress;
	private Rect eraser;
	private float ex,ey,ex2,ey2 = -1;
	private boolean draw = false;
	private boolean saving = false;
	private boolean trysave = false;
	private boolean trywarp = false;

	public World() throws SlickException{super();}

	/**
	 * Initializes platforms and player location.
	 * @author VincentLy
	 */
	public void init(GameContainer gc,StateBasedGame sbg) throws SlickException { 
		Things.loader = new WorldLoader(Things.file);
		Things.platforms = Things.loader.getPlatforms();
		if(Settings.prevState!=Settings.sID)
			hotspots = Things.loader.getPortals();
		savept = Things.loader.getSavePt();
		if(Things.player!=null)
			Things.player.move(Things.x,Things.y-100);
		Things.projectiles.clear();		
	}

	/**
	 * Updates the objects in the world as well as scene change and input.
	 * @author VincentLy
	 */
	public void update(GameContainer gc,StateBasedGame sbg, int delta) throws SlickException {
		switch(Things.warps){
		case 3:
			hotspots.add(new Rect(150,100,100,100,0));
			Things.warps++;
			break;
		case 5:
			hotspots.add(new Rect(50,-115,100,100,0));
			Things.warps++;
			break;
		case 8:
			hotspots.add(new Rect(220,-1658,100,100,0));
			Things.warps++;
			break;
		}		
		//handle warping
		warpHandler(gc,sbg);
		trywarp = false;
		//handle inputs
		handleInput(gc,sbg);  
		//handle saving
		if(trysave && Things.player.intersects(savept)){
			save();			
			saving = true;
			savetime = System.currentTimeMillis();
		}	
		trysave = false;
		//update platforms
		for(Platform p:Things.platforms)
			if(p instanceof MovingPlatform)
				((MovingPlatform) p).update();
		//update projectiles
		for(Projectile p:Things.newprojectiles)
			Things.projectiles.add(p);
		Things.newprojectiles.clear();
		for(Projectile p:Things.projectiles)
			p.update();
		//update player
		Things.player.update();     
		//check for platform collisions
		UsefulMethods.platformCollisions(Things.player);	
		//check for projectile collisions
		UsefulMethods.bulletCollisions(gc,sbg);
		//check for screen scrolling
		float[] chg = UsefulMethods.sceneChange(Things.scene,Things.player);
		Settings.tx+=chg[0];
		Settings.ty+=chg[1];	
	}

	/**
	 * Handles warping in the world.
	 * @param gc - GameContainer
	 * @param sbg - StateBasedGame
	 * @author VincentLy
	 * @throws SlickException 
	 * @throws FileNotFoundException 
	 */
	public void warpHandler(GameContainer gc,StateBasedGame sbg) throws SlickException{
		boolean battle = true;
		Rect warp = null;
		for(Rect r:hotspots)
			if(Things.player.intersects(r)){
				warp = r;
				break;
			}
		if(warp!=null){
			Things.x = Things.player.x();
			Things.y = Things.player.y();
			switch((int)warp.left()){
			case 25:
				Things.enemies.add(new JumpingEnemy(400,200,1,Settings.enemy));
				Things.enemies.add(new JumpingEnemy(600,300,1,Settings.enemy));
				Things.enemies.add(new ShootingEnemy(300,100,1,Settings.enemy));
				break;
			case 50:
				if(trywarp){
					Things.player.move(250,-100);
					load("res/world3platforms.txt",3);
				}
				battle = false;
				break;
			case 150:
				if(trywarp)
					Things.player.move(150,-200);
				battle = false;
				break;
			case 220:
				Things.enemies.add(new BossEnemy(400,100,1,Settings.enemy));
				break;
			case 380:
				Things.enemies.add(new MovingEnemy(600,400,1,Settings.enemy));
				Things.enemies.add(new MovingEnemy(200,400,1,Settings.enemy));
				Things.enemies.add(new ShootingEnemy(400,100,1,Settings.enemy));
				break;
			case 400:
				Things.enemies.add(new StatueEnemy(400,300,1,Settings.enemy));	
				break;
			case 475:
				if(trywarp){
					Things.player.move(400,-100);
					load("res/world2platforms.txt",2);
				}
				battle = false;
				break;
			case 600:
				Things.enemies.add(new MovingEnemy(200,300,1,Settings.enemy));
				Things.enemies.add(new MovingEnemy(400,200,1,Settings.enemy));
				Things.enemies.add(new MovingEnemy(600,300,1,Settings.enemy));
				break;
			case 825:
				Things.enemies.add(new ShootingEnemy(400,100,1,Settings.enemy));
				break;
			case 875:
				Things.enemies.add(new JumpingEnemy(200,300,1,Settings.enemy));
				Things.enemies.add(new JumpingEnemy(400,200,1,Settings.enemy));
				Things.enemies.add(new JumpingEnemy(600,300,1,Settings.enemy));
				break;
			}
			if(trywarp)
				hotspots.remove(warp);
			if(battle){
				hotspots.remove(warp);
				Things.warps++;
				warp = null;
				Things.player.setXVel(0);
				Things.player.setYVel(0);
				sbg.getState(Settings.sID).init(gc,sbg);
				sbg.enterState(Settings.sID);
			}
			warp = null;
		}	
	}
	
	/**
	 * Loads the next level/world.
	 * @param s - file name
	 * @param w - world number
	 */
	public void load(String s,int w){
		Things.platforms.clear();
		Things.file = s;
		Things.loader = new WorldLoader(Things.file);
		Things.platforms = Things.loader.getPlatforms();
		hotspots = Things.loader.getPortals();
		savept = Things.loader.getSavePt();
		Things.x = Things.player.x();
		Things.y = Things.player.y();
		Things.world = w;
	}
	
	/**
	 * Saves important data to a text file.
	 * @author VincentLy
	 */
	public void save(){
		Things.x = Things.player.x();
		Things.y = Things.player.y();
		try {
			pw = new PrintWriter(new File("res/save.txt"));
			pw.println(Things.world);
			pw.println(Things.warps);
			pw.println(Things.x);
			pw.println(Things.y);
			pw.println(Things.player.hp());
			pw.println(Things.gold);
			pw.close();
		} 
		catch (FileNotFoundException e){System.out.println("Saving problem.");}
	}
	
	/**
	 * Specifically handles player input.
	 * @param gc - GameContainer
	 * @param sbg - StateBasedGame
	 * @throws SlickException
	 * @author VincentLy
	 */
	public void handleInput(GameContainer gc,StateBasedGame sbg) throws SlickException{
		Input input = gc.getInput(); 
		if(!Things.player.isHit()){ 
			//jumping
			if(input.isKeyPressed(Input.KEY_W)){  
				AttackQ.add("w");
				if(Things.player.jumps()>0){
					Things.player.setPlatform(null);
					if(Things.player.jumps()==2)
						Things.player.setYVel(60);    
					else if(Things.player.jumps()==1)
						Things.player.setYVel(50);
					Things.player.setJumps(Things.player.jumps()-1);
					Things.player.jump();
				}				
			}	    	
			if(!Things.player.isUsingMove() && !Things.player.isDucking()){
				//moving left
				if(input.isKeyDown(Input.KEY_A)){
					Things.player.setXVel(-30);    		
					Things.player.setDir(-1); 
					Things.player.walkBwd();
				}    	
				//moving right
				else if(input.isKeyDown(Input.KEY_D)){
					Things.player.setXVel(30);    	
					Things.player.setDir(1);
					Things.player.walkFwd();
				}   
				else
					Things.player.stop();
			}
		}
		//crouching
		if(input.isKeyDown(Input.KEY_S)){
			if(Things.player.platform()!=null)
				Things.player.duck();
			Things.player.setYVel(Things.player.yvel()-1);
		}
		else 
			Things.player.rise();
//		if(input.isKeyPressed(Input.KEY_B))
//			draw = !draw;
		//music
		if(input.isKeyPressed(Input.KEY_M))
			gc.setMusicOn(!gc.isMusicOn()); 
		//pausing
		if(input.isKeyPressed(Input.KEY_P))
			sbg.enterState(Settings.pmID);
		//warping or saving
		if(input.isKeyPressed(Input.KEY_J)){
			trywarp = true;	
			trysave = true;
		}	
		if(input.isKeyPressed(Input.KEY_S))
			AttackQ.add("s");
		if(input.isKeyPressed(Input.KEY_A))
			AttackQ.add("a");
		if(input.isKeyPressed(Input.KEY_D))
			AttackQ.add("d");
		if(draw){
			if(input.isMousePressed(Input.MOUSE_RIGHT_BUTTON)){
				ex = input.getMouseX() - Settings.tx;
				ey = input.getMouseY() - Settings.ty;
			}	
			else if(input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)){
				ex2 = input.getMouseX() - Settings.tx;
				ey2 = input.getMouseY() - Settings.ty;
				if(ex2<ex)
					if(ey2<ey)
						eraser = new Rect(ex2,ey2,ex-ex2,ey-ey2,5);
					else
						eraser = new Rect(ex2,ey,ex-ex2,ey2-ey,5);
				else
					if(ey2<ey)
						eraser = new Rect(ex,ey2,ex2-ex,ey-ey2,5);
					else
						eraser = new Rect(ex,ey,ex2-ex,ey2-ey,5); 			
			}
			else if(eraser!=null){
				ArrayList<Platform> conflicts = new ArrayList<Platform>();
				for(Platform p:Things.platforms)
					if(eraser.intersects(p))
						conflicts.add(p);
				for(Platform p:conflicts)
					Things.platforms.remove(p);
				conflicts.clear();
				eraser = null;
			}
			else if(input.isMousePressed(Input.MOUSE_LEFT_BUTTON)){
				ex = input.getMouseX()-Settings.tx;
				ey = input.getMouseY()-Settings.ty;       						
			}
			else if(input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON)){   
				if(!input.isKeyDown(Input.KEY_LCONTROL)){
					ex2 = input.getMouseX() - Settings.tx;
					ey2 = input.getMouseY() - Settings.ty;
					if(ex2<ex)
						if(ey2<ey)
							progress = new Platform(ex2,ey2,ex-ex2,ey-ey2,5,1);
						else
							progress = new Platform(ex2,ey,ex-ex2,ey2-ey,5,1);
					else
						if(ey2<ey)
							progress = new Platform(ex,ey2,ex2-ex,ey-ey2,5,1);
						else
							progress = new Platform(ex,ey,ex2-ex,ey2-ey,5,1);    	
				}
			}
			else if(progress!=null){
				Things.platforms.add(progress); 
				//System.out.println((int)progress.left() + "," + (int)progress.top() + "," + (int)progress.width() + "," + (int)progress.height());
				progress = null;    	
			}
		}
	}

	/**
	 * Draws the objects in the world as well as screen text and animations.
	 * @author VincentLy
	 */
	public void render(GameContainer gc,StateBasedGame sbg, Graphics g) throws SlickException{     	
		g.setAntiAlias(true);
		g.setBackground(Settings.bg);
		g.translate(Settings.tx,Settings.ty);
		Settings.portal.rotate(.1f);
		for(Rect r:hotspots){
			if(r.color()==Color.red)
				Settings.door.draw(r.left(),r.top()-25);
			else
				Settings.portal.draw(r.left(),r.top()+10);
		}	
		Settings.save.draw(savept.left()-35,savept.top());
		g.setColor(Color.white);
		if(saving && System.currentTimeMillis()-savetime<2000)
			g.drawString("Saved",-Settings.tx+Settings.screenX/2-g.getFont().getWidth("Saved")/2,-Settings.ty+300);
		for(Platform p:Things.platforms)
			g.texture(p,Settings.texture,true);
		Things.player.draw(g);
		g.setColor(Color.white);
		if(progress!=null)
			g.draw(progress); 
		if(eraser!=null)
			g.draw(eraser);
		UsefulMethods.map(g);
		g.setColor(Color.white);
		if(System.currentTimeMillis()-Settings.time<2500)
			g.drawString("World " + Things.world,-Settings.tx+Settings.screenX/2-g.getFont().getWidth("World" + Things.world)/2,-Settings.ty+100);
	}

	/**
	 * @return id
	 * @author VincentLy
	 */
	public int getID() {return Settings.wID;}    
}